package org.nutz.lang.born;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import org.nutz.castor.Castors;
import org.nutz.lang.Lang;
import org.nutz.lang.MatchType;
import org.nutz.lang.Mirror;

/**
 * 关于创建对象的一些帮助方法
 * 
 * @author zozoh(zozohtnt@gmail.com)
 */
public abstract class Borns {

	/**
	 * 根据参数类型数组获取一个对象的构建信息
	 * 
	 * @param <T>
	 *            对象类型信息
	 * @param type
	 *            对象类型
	 * @param argTypes
	 *            构造参数类型数组
	 * @return 构建信息对象
	 */
	public static <T> BornContext<T> evalByArgTypes(Class<T> type, Class<?>... argTypes) {
		BornContext<T> re;
		if (argTypes.length == 0) {
			re = evalWithoutArgs(type);
		} else {
			re = evalWithArgTypes(true, type, argTypes, null);
		}
		return re;
	}

	/**
	 * 根据参数类型数组获取一个对象的构建信息
	 * 
	 * @param <T>
	 *            对象类型信息
	 * @param type
	 *            对象类型
	 * @param args
	 *            构造参数数组
	 * @return 构建信息对象
	 * @throws NullPointerException
	 *             when args is null
	 */
	public static <T> BornContext<T> eval(Class<T> type, Object... args) {
		BornContext<T> re;
		if (args.length == 0) {
			re = evalWithoutArgs(type);
		} else {
			re = evalWithArgs(type, args);
		}
		return re;
	}

	/**
	 * 根据一个调用参数数组，获取一个对象的构建信息
	 * 
	 * @param <T>
	 *            对象类型信息
	 * @param type
	 *            对象类型
	 * @param args
	 *            参考构建参数
	 * @return 构建信息对象
	 */
	private static <T> BornContext<T> evalWithArgs(Class<T> type, Object[] args) {
		// 准备变参数组
		Object dynaArg = Mirror.evalArgToSameTypeRealArray(args);

		// 准备好参数类型
		Class<?>[] argTypes = Mirror.evalToTypes(args);

		BornContext<T> re = evalWithArgTypes(false, type, argTypes, dynaArg);

		if (null == re)
			return null;

		if (MatchType.LACK == re.getMatchType()) {
			re.setArgs(Lang.arrayLast(args, re.getLackArg()));
		} else {
			re.setArgs(args);
		}

		switch (re.getMatchType()) {
		case LACK:
			re.setArgs(Lang.arrayLast(args, re.getLackArg()));
			break;
		case NEED_CAST:
			re.setArgs(Lang.array2ObjectArray(args, re.getCastType()));
			break;
		default:
			re.setArgs(args);
		}

		return re;
	}

	/**
	 * 根据一个调用参数类型数组，获取一个对象的构建信息
	 * 
	 * @param <T>
	 *            对象类型信息
	 * @param accurate
	 *            是否需要精确匹配
	 * @param type
	 *            对象类型
	 * @param argTypes
	 *            参考参数类型数组
	 * @param dynaAry
	 *            参考参数类型信息是否是一个变参数组
	 * @return 构建信息对象
	 */
	@SuppressWarnings({"rawtypes", "unchecked"})
	private static <T> BornContext<T> evalWithArgTypes(boolean accurate,
													   Class<T> type,
													   Class<?>[] argTypes,
													   Object dynaArg) {
		// 准备好返回对象
		BornContext<T> re = new BornContext<T>();

		// 先看看有没对应的构造函数
		Mirror<T> mirror = Mirror.me(type);
		for (Constructor<?> cc : type.getConstructors()) {
			Class<?>[] pts = cc.getParameterTypes();
			MatchType mt = Mirror.matchParamTypes(pts, argTypes);
			re.setMatchType(mt);
			// 正好合适
			if (MatchType.YES == mt) {
				return re.setBorning(new ConstructorBorning(cc));
			}
			// 差一个参数，说明这个构造函数有变参数组
			else if (MatchType.LACK == mt) {
				re.setLackArg(Mirror.blankArrayArg(pts));
				return re.setBorning(new ConstructorBorning(cc));
			}
			// 看看整个输入的参数是不是变参
			else if (null != dynaArg && pts.length == 1 && pts[0] == dynaArg.getClass()) {
				return re.setBorning(new DynamicConstructorBorning(cc));
			}
		}

		// 看看有没有对应静态工厂函数
		Method[] sms = mirror.getStaticMethods();
		for (Method m : sms) {
			Class<?>[] pts = m.getParameterTypes();
			MatchType mt = Mirror.matchParamTypes(pts, argTypes);
			re.setMatchType(mt);
			if (MatchType.YES == mt) {
				return re.setBorning(new MethodBorning<T>(m));
			} else if (MatchType.LACK == mt) {
				re.setLackArg(Mirror.blankArrayArg(pts));
				return re.setBorning(new MethodBorning<T>(m));
			} else if (null != dynaArg && pts.length == 1) {
				if (pts[0] == dynaArg.getClass()) {
					return re.setBorning(new DynaMethodBorning<T>(m));
				}
			}
		}
		// 如果不是要精确查找的话
		if (!accurate) {
			// 找到一个长度合适的构造函数，准备转换
			try {
				for (Constructor<?> cc : type.getConstructors()) {
					Class<?>[] pts = cc.getParameterTypes();
					if (canBeCasted(argTypes, pts)) {
						re.setMatchType(MatchType.NEED_CAST);
						re.setCastType(pts);
						return re.setBorning(new ConstructorCastingBorning(cc));
					}
				}
			}
			catch (RuntimeException e) {}
			// 有没有变参的静态构造方法
			try {
				for (Method m : sms) {
					Class<?>[] pts = m.getParameterTypes();
					if (canBeCasted(argTypes, pts)) {
						re.setMatchType(MatchType.NEED_CAST);
						re.setCastType(pts);
						return re.setBorning(new MethodCastingBorning<T>(m));
					}
				}
			}
			catch (Exception e) {}
		}

		return null;
	}

	private static boolean canBeCasted(Class<?>[] argTypes, Class<?>[] pts) {
		if (pts.length != argTypes.length)
			return false;
		for (int i = 0; i < pts.length; i++) {
			if (!Castors.me().canCast(argTypes[i], pts[i]))
				return false;
		}

		return true;
	}

	/**
	 * 为一个给定类，寻找一个不需要参数的构造方法
	 * 
	 * @param <T>
	 *            类
	 * @param type
	 *            类实例
	 * @return 构造信息
	 */
	@SuppressWarnings({"rawtypes", "unchecked"})
	private static <T> BornContext<T> evalWithoutArgs(Class<T> type) {
		// 准备好返回对象
		BornContext<T> re = new BornContext<T>();
		Mirror<T> mirror = Mirror.me(type);
		boolean isAbstract = Modifier.isAbstract(type.getModifiers());

		// 先看看有没有默认构造函数
		try {
			if (!isAbstract) {
				re.setBorning(new EmptyArgsConstructorBorning<T>(type.getConstructor()));
				return re.setArgs(new Object[0]);
			}
		}
		// 如果没有默认构造函数 ...
		catch (Exception e) {}
		// 看看有没有默认静态工厂函数
		Method[] stMethods = mirror.getStaticMethods();
		for (Method m : stMethods) {
			if (m.getReturnType().equals(type) && m.getParameterTypes().length == 0) {
				return re.setBorning(new EmptyArgsMethodBorning<T>(m)).setArgs(new Object[0]);
			}
		}
		// 看看有没有带一个动态参数的构造函数
		if (!isAbstract) {
			for (Constructor<?> cons : type.getConstructors()) {
				Class<?>[] pts = cons.getParameterTypes();
				if (pts.length == 1 && pts[0].isArray()) {
					Object[] args = new Object[1];
					args[0] = Mirror.blankArrayArg(pts);
					return re.setBorning(new ConstructorBorning(cons)).setArgs(args);
				}
			}
		}
		// 看看有没有带一个动态参数的静态工厂函数
		for (Method m : stMethods) {
			Class<?>[] pts = m.getParameterTypes();
			if (m.getReturnType() == type && m.getParameterTypes().length == 1 && pts[0].isArray()) {
				Object[] args = new Object[1];
				args[0] = Mirror.blankArrayArg(pts);
				return re.setBorning(new MethodBorning<T>(m)).setArgs(args);
			}
		}

		return null;
	}

}
